//
//  HTTPClient.m
//  Pure Dates
//
//  Created by Jiang on 2018/3/20.
//  Copyright © 2018年 Jiang. All rights reserved.
//
#import "HTTPClient.h"
#import "HTTPPlugin.h"
static NSString *queryPramameters(NSDictionary *param){
    NSMutableArray *pairs = [NSMutableArray array];
    [param enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        [pairs addObject:[NSString stringWithFormat:@"%@=%@",key,obj]];
    }];
    return [pairs componentsJoinedByString:@"&"];
}
@implementation HTTPClient{
    NSURL * _baseUrl;
}
- (instancetype)initWithBaseUrl:(NSURL *)baseUrl{
    self = [super init];
    self->_baseUrl = baseUrl;
    return self;
}
- (NSURLSessionDataTask *)requestWithMethod:(SFNHTTPMethod)method
                                        uri:(NSString *)URLString
                                 parameters:(nullable NSDictionary *)param
                                    plugin:(nullable id <SFNPlugin>)plugin
                                 completion:(HTTPHandler)completion{
    
    NSMutableURLRequest *request = [self createRequestWithMethod:method uri:URLString parameters:param];
    return [self request:request plugin:plugin completion:completion];
}

- (NSMutableURLRequest *)createRequestWithMethod:(SFNHTTPMethod)method uri:(NSString *)uri parameters:(nullable NSDictionary *)param{
    NSURL *url = [self.baseUrl URLByAppendingPathComponent:uri];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.timeoutInterval = 15;
    NSMutableDictionary *signedParam = [param mutableCopy];
    
    if (method == POST){
        request.HTTPMethod = @"POST";
        request.HTTPBody = [queryPramameters(signedParam) dataUsingEncoding:NSUTF8StringEncoding];
    }else{
        request.HTTPMethod = @"GET";
//        request.URL = [url.absoluteString stringByAppendingString:[@"?" stringByAppendingString:queryPramameters(signedParam)]].URL;
    }
    return request;
}
- (NSMutableURLRequest *)createEncryptedRequestWithMethod:(SFNHTTPMethod)method uri:(NSString *)uri parameters:(NSDictionary *)param{
    NSURL *url = [self.baseUrl URLByAppendingPathComponent:uri];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.timeoutInterval = 15;
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    request.HTTPMethod = @"POST";
    NSData *originalData = [NSJSONSerialization dataWithJSONObject:param options:0 error:nil];
    NSString *jsonStr = [[NSString alloc] initWithData:originalData encoding:NSUTF8StringEncoding];
    // AES 256 CBC
    // Base64_Salt: base64((char ^ 0xF))
    // key = randomString(32)
    // key1 = key.subStringWithRange(0, 16)
    // key2 = key.subStringWithRange(16, 16)
    // iv = randomString(16)
    // sign = MD5_SALT(key1+key2+iv)5
    NSDictionary *container = [NSDictionary dictionary];
    request.HTTPBody = [NSJSONSerialization dataWithJSONObject:container options:0 error:nil];
    
    return request;
}

- (NSURLSessionDataTask *)request:(NSURLRequest *)request
                          plugin:(nullable id <SFNPlugin>)plugin
                       completion:(HTTPHandler)completion{
    
    // 如果是转换操作：result = model | [model]。handleWithRequest:Reponse:data:error:
    // 比如 serilation JSON序列化操作: NSData -> JSON . return JSON
    // 比如 check 判断服务器返回数据是否错误： JSON -> 判断 . return JSON
    // 比如 log 错误请求打印:  Requeset, Reponse, JSON -> Log . return JSON
    // 比如 model 模型转换: JSON -> Model . return Model | [Model]
    
    // 如果是缓存操作: 保存内存或者磁盘，直接返回 result。cachedDataForRequest: handleWithRequest:Reponse:data:error:
    
    // 如果是Fake操作: 只需实现testDataForRequest:completionHandler:
    
    // 如果是UI操作: 直接翻译 result，在相应的步骤介入即可
    // 比如 加载框，开始请求时显示，结束时消失
    // 比如 提示框, 请求错误时，显示提示
    // 比如 空白展位图，分页请求时，NSData->json->PageModel->检查所有页数，总页数为0时，显示
    
    NSMutableArray *plugins = [NSMutableArray array];
    while (plugin) {
        [plugins addObject:plugin];
        plugin = plugin.next;
    }
    
    void (^completeHandler)(NSData *data, NSURLResponse *response, NSError *error) = ^(NSData *data, NSURLResponse *response, NSError *error){
        NSHTTPURLResponse *httpReponse = (NSHTTPURLResponse *)response;
        id result = data;
        for (id <SFNPlugin>plugin in plugins) {
            if ([plugin respondsToSelector:@selector(handleWithRequest:Reponse:data:error:)]) {
                // 如果是缓存、此时不调用缓存插件的保存方法
                if (!response && [plugin respondsToSelector:@selector(cachedDataForRequest:)]) {
                    continue;
                }
                result = [plugin handleWithRequest:request Reponse:httpReponse data:result error:&error];
            }
        }
        completion(httpReponse,result,error);
    };
    
    // check cache
    for (id <SFNPlugin>plugin in plugins) {
        if ([plugin respondsToSelector:@selector(cachedDataForRequest:)]) {
            id cahce = [plugin cachedDataForRequest:request];
            completeHandler(nil, cahce, nil);
            return nil;
        }
    }
    __weak typeof(plugins) weakPlugins  = plugins;
    NSURLSessionDataTask* (^resumeTask)(void) = ^NSURLSessionDataTask *{
        for (id <SFNPlugin>plugin in weakPlugins) {
            if ([plugin respondsToSelector:@selector(willStart:)]) {
                [plugin willStart:request];
            }
        }
        for (id <SFNPlugin>plugin in weakPlugins){ // send test data before task resume
            if ([plugin respondsToSelector:@selector(testDataForRequest:completionHandler:)]) {
                [plugin testDataForRequest:request completionHandler:^(NSData *data, NSError *error) {
                    completeHandler(data, [NSHTTPURLResponse new],error);
                }];
                return nil;
            }
        }
        NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:completeHandler];
        [task resume];
        return task;
    };
    
    // set plugin's retry block
    for (id <SFNPlugin>plugin in plugins) {
        if ([plugin respondsToSelector:@selector(setRetryClosure:)]) {
            [plugin setRetryClosure:resumeTask];
            
        }
    }
    return resumeTask();
}
@end
